Tutorial step 8: Adding job (batch) support |
Only one major feature is missing from our filter: job support. As it stands, our filter will revert to default parameters if it is used in a batch operation. Time to fix that.
How VirtualDub batch operation works with filters |
If you look in a virtualdub.jobs file, you'll find lines like this:
VirtualDub.video.filters.Add("rotate2");
VirtualDub.video.filters.instance[0].Config(233017,2);
The first line adds the filter to the list, while the second configures filter-specific parameters. Your filter is responsible for the second line only, through the script_obj and fssProc fields of the FilterDefinition structure. When the job is run, the lines are interpreted by a primitive C-like scripting language called Sylia. The tutorial filter will interface to Sylia and add methods to the VirtualDub object tree.
Custom objects in the Sylia scripting language |
This is the CScriptObject structure:
typedef struct CScriptObject {
ScriptObjectLookupFuncPtr Lookup;
ScriptFunctionDef *func_list;
ScriptObjectDef *obj_list;
} CScriptObject;
An object can hold methods, other objects, or even variables. Because of limitations on the configuration strings that filters can generate, we're only interested in adding Config() functions to our object, and thus the Lookup and obj_list will be NULL.
func_list points to an array of function definitions:
typedef struct ScriptFunctionDef {
ScriptFunctionPtr func_ptr;
char *name;
char *arg_list;
} ScriptFunctionDef;
arg_list describes the function's return type and parameters. The first character should be 0 (zero) for void; subsequent characters should be i for an int and s for a string. Thus, "0iis" becomes the function template (int, int, string). You can also end the parameter list with . (period) to specify a variable argument list; anything will do for the remainder.
To specify overloaded members, add one entry with a name and an arg_list, and follow up with more entries that have NULL for name. The script interpreter will pick the first one that fits. End the function list with an all NULL entry. A member list might look like this:
ScriptFunctionDef tutorial_func_defs[]={
{ (ScriptFunctionPtr)tutorialScriptConfig, "Config", "0ii" },
{ (ScriptFunctionPtr)tutorialScriptConfig, NULL, "0iii." },
{ (ScriptFunctionPtr)tutorialScriptConfig, NULL, "0is" },
{ NULL },
};
This defines three methods: void Config(int, int), void Config(int, int, int, ...), and void Config(int, string). All definitions point to the same handler, which would have to check the parameter types to discover which signature fits.
Implementing the Config() function |
All Config() script functions have the following prototype:
CScriptValue *configScriptProc(IScriptInterpreter *isi, void *fa_ptr, CScriptValue *argv, int argc);
fa_ptr should be cast to FilterActivation * so you can get to your instance data pointer, and argc/argv contains the input parameters. Use:
Of course, argc is the argument count. The parameter list is guaranteed to fit one of the signatures specified for this function, so often it's easiest to point all the overloaded members to a single C++ function, and use the is* functions to determine which signature is being used.
On errors, use the macro EXT_SCRIPT_ERROR(error). This uses the C++ throw mechanism. Use one of the following constants:
Implementing the fssProc function |
fssProc's job is to generate the script function call and has this prototype:
bool fssProc(FilterActivation *fa, const FilterFunctions *ff, char *buf, int buflen);
This function should use sprintf(), wsprintf(), or preferably _snprintf() to generate the config string. VirtualDub provides the string up to instance[n]. and the semicolon, so fssProc need only fill buf with Config(...). For instance, a resampling filter with a size of 240x180 might fill buf with this:
Config(240,180)
fssProc should normally return true. If fssProc returns false, VirtualDub ignores buf and discards the entire configuration line. This is useful if the filter instance hasn't been changed from the default configuration.
Note that you must be careful when adding strings to your configuration function. In particular, watch out for backslashes and quotes. Sylia understands C/C++ escape sequences, so the safest bet is preprocessing those characters into \" and \\. If you must include binary data, the best bet is to encode the data into a safe format such as MIME BASE64.
Adding script support to the tutorial filter |
We'll use a Config(int, int) script function to control our filter, with one int for each flag:
bool tutorialFssProc(FilterActivation *fa, const FilterFunctions *ff, char *buf, int buflen) {
MyFilterData *mfd = (MyFilterData *)fa->filter_data;
_snprintf(buf, buflen, "Config(%d, %d)",
mfd->fExpand,
mfd->fThird);
return true;
}
Now, the implementation of the Config() function:
void tutorialScriptConfig(IScriptInterpreter *isi, void *lpVoid, CScriptValue *argv, int argc) {
FilterActivation *fa = (FilterActivation *)lpVoid;
MyFilterData *mfd = (MyFilterData *)fa->filter_data;
mfd->fExpand = !!argv[0].asInt();
mfd->fThird = !!argv[1].asInt();
}
ScriptFunctionDef tutorial_func_defs[]={
{ (ScriptFunctionPtr)tutorialScriptConfig, "Config", "0ii" },
{ NULL },
};
CScriptObject tutorial_obj={
NULL, tutorial_func_defs
};
Finally, add the scripting support to the FilterDefinition struct:
&tutorial_obj, // script_obj
tutorialFssProc, // fssProc
Additional notes |
back to main page
tutorial[7]: adding MMX optimizations
dword ptr [tutorial+36]: aftermath
VirtualDub external filter SDK 1.05 | ©1999-2001 Avery Lee <phaeron@virtualdub.org> |